#include "c3GUI.h"
#include "c3Engine.h"
#include "c3Constants.h"
#include "ext/tinyxml.h"

struct GUILoader
{
	TiXmlElement* CurrentNode;
	GUIControl* Load(const char* fname)
	{
		TiXmlDocument doc(fname);
		if ( !doc.LoadFile() ) return 0;

		CurrentNode = doc.FirstChildElement();
		return LoadControl(CurrentNode);
	}
	GUIControl* LoadControl(TiXmlElement* element)
	{/*
		GUIControl* ctrl = 0;
		if (!stricmp(element->Value(), "control")) ctrl = new GUIControl;

		if (!ctrl) return 0;

		CurrentNode = element;
		ctrl->Load(*this);

		TiXmlElement* after = 0;
		for (TiXmlNode* child = element->FirstChild(); child; child = child->NextSibling())
		{
			TiXmlElement* elem = child->ToElement();
			if (!elem) continue;

			if (!stricmp(elem->Value(), "after"))
				after = elem;
			else
			{
				GUIControl* childControl = LoadControl(elem);
				if (childControl)
				{
					ctrl->AddChild(childControl);
				}
			}
		}

		if (after)
			DoAfter(ctrl, after);

		return ctrl;*/
		return 0;
	}
	void DoAfter(GUIControl* control, TiXmlElement* after)
	{

	}
	template <typename T>
	void GetProperty(const char* pname, T& val)
	{
		val = CurrentNode->QueryValueAttribute<T>(pname, &val);
	}
	const char* GetAttrib(const char* pname)
	{
		return CurrentNode->Attribute(pname);
	}
};

///************************************************************************/
///* GUIControl                                                           */
///************************************************************************/

Sound* mClickSound;

void GUIControl::SetClickSound(Sound* sound)
{
  mClickSound = sound;
}

GUIControl::GUIControl()
{
  TheImage = 0;

  mPressed = false;

  mFlags |= ObjectFlags::Interactive;

  Effects.ColorFilter.Color = 0xff808080;
}

bool GUIControl::MouseDown(int x, int y)
{
  if (!Enabled) return false;

  if (MouseChildren)
  {
    int numChildren = mChildren.size();
    for (int i=numChildren-1; i>=0; --i) // mouse events are front to back
    {
      if (mChildren[i] && mChildren[i]->GetFlags()&ObjectFlags::Interactive)
      {
        GUIControl* control = (GUIControl*)mChildren[i];
        if (control->MouseDown(x-X, y-Y)) // jesli dziecko obsluzylo
          return true;
      }
    }
  }
  if (!IsPointInside(x,y,true)) return false;

  if (OnMouseDown(x-X, y-Y))
    return true; // obsluzone
  else
    return false; // nieobsluzone
}

bool GUIControl::MouseUp(int x, int y)
{
  if (!Enabled) return false;
  
  if (MouseChildren)
  {
    int numChildren = mChildren.size();
    for (int i=numChildren-1; i>=0; --i) // mouse events are front to back
    {
      if (mChildren[i] && mChildren[i]->GetFlags()&ObjectFlags::Interactive)
      {
        GUIControl* control = (GUIControl*)mChildren[i];
        control->MouseUp(x-X, y-Y);
      }
    }
  }

  OnMouseUp(x, y);

  return true;
}

bool GUIControl::OnMouseDown(int x, int y)
{
  if (Enabled)
  {
    AddEvent(GUIEvent::MouseDown);
    mPressed = true;
    return true;
  } else return false;
}

bool GUIControl::OnMouseUp(int x, int y)
{
  if (mPressed && IsPointInside(x, y, true))
  {
    AddEvent(GUIEvent::MouseUp);
    mPressed = false;
    return OnClick();
  }

  mPressed = false;
  return false;
}

bool GUIControl::OnClick()
{
  AddEvent(GUIEvent::Click);
  return true;
}

GUIControl& GUIControl::Set( Image* image, int x, int y )
{
  TheImage = image;
  SetDimsFromImage();
  X = x;
  Y = y;

  return *this;
}

void GUIControl::SetDisplay( bool display )
{
  Visible = display;
  Enabled = display;
}

void GUIControl::Load( GUILoader& loader )
{
	loader.GetProperty("x", X);
	loader.GetProperty("y", Y);
}

///************/
///* Button   */
///************/

Button::Button()
{
  mFont = 0;
  TextColor = 0xffffffff;
  mType = Normal;
  mLabel = 0;
  mLabelLen = 0;

  mPressed = false;
	mActive = false;

  mFrame = 0;
}

int Button::GetImageState()
{
  if (!Enabled) return 2;
  if (mPressed) return 3;
	if ( mActive) return 1;
  return 0;
}

void Button::SetType(ButtonType type)
{
  mType = type;
}

void Button::SetLabel( const wchar_t* label )
{
  mLabel = label;
  if (mFont && mLabel)
    mLabelLen = mFont->len(label)/2;
}

void Button::SetFont(Font* font)
{
  mFont = font;
  if (mFont && mLabel)
    mLabelLen = mFont->len(mLabel)/2;
}

Button& Button::Set( Font& font, Image& image, const wchar_t* label, int x, int y, ButtonType type /*= Button::Normal*/ )
{
  mFont = &font;
  TheImage = &image;
  SetLabel(label);
  X = x;
  Y = y;
  Width = image.dx;
  Height = image.dy/4;
	mType = type;

  return *this;
}

uint Button::DrawAt(int x, int y, uint8 alpha)
{
  if (TheImage)
  {
    int curFrame = 0;
    if (TheImage->dx != Width)
      curFrame = (mFrame%((TheImage->dx)/Width)); // animate buttons
    TheImage->saypx(x, y, curFrame*Width, GetImageState()*Height, Width, Height, Multiply8(Alpha, alpha), Rotation, Color);
  }
  if (mLabel && mFont && *mLabel)
  {
    int off = mPressed?1:0;
    uint8 outAlpha = Multiply8((Enabled?(unsigned char)255:(unsigned char)192), alpha);
    mFont->textpx(mLabel, off+x+Width/2-mLabelLen, off+y+(Height/2)-(mFont->get_font_height()/2), 0, 0, outAlpha, TextColor);
  }

  return 0;
}

bool Button::OnMouseDown(int x, int y)
{
  if (Enabled && TheImage/* && GetAlpha(*TheImage, x, y)>10*/)
  {
    AddEvent(GUIEvent::MouseDown);
    mPressed = true;
    return true;
  } else return false;
}

bool Button::OnMouseUp(int x, int y)
{
  if (mPressed && IsPointInside(x, y, true))
  {
    AddEvent(GUIEvent::MouseUp);
    mPressed = false;
    return OnClick();
  }

  mPressed = false;
  return false;
}

bool Button::OnClick()
{
  AddEvent(GUIEvent::Click);

	if (mType == CheckButton)
		mActive = !mActive;
	if (mType == RadioButton)
	{
		for (uint i=0; i<this->mParent->GetNumChildren(); i++)
		{
  		DisplayObject* disp = this->mParent->GetChildAt(i);
			if (disp->Is('butt') || disp->Is('imbu'))
			{
				Button* b = (Button*)disp;
				if (b->mType == RadioButton)
					b->mActive = false;
			}
		}
		mActive = true;
	}

  mClickSound->play();

  return true;
}

///***************/
///* ImageButton */
///**************/

ImageButton::ImageButton() : Button(), ButtonImage(0)
{
}

ImageButton& ImageButton::Set( Image& image, Image* butImage, int x, int y, Button::ButtonType type /*= Button::Normal*/ )
{
	TheImage = &image;
	ButtonImage = butImage;
	X = x;
	Y = y;
	Width = image.dx;
	Height = image.dy/4;
	mType = type;

	return *this;
}

uint ImageButton::DrawAt(int x, int y, uint8 alpha)
{
	if (TheImage)
	{
		int curFrame = 0;
		if (TheImage->dx != Width)
			curFrame = (mFrame%((TheImage->dx)/Width)); // animate buttons
		TheImage->saypx(x, y, curFrame*Width, GetImageState()*Height, Width, Height, Multiply8(Alpha, alpha), Rotation, Color);
	}
	if (ButtonImage)
	{
		int off = mPressed?1:0;
		uint8 outAlpha = Multiply8((Enabled?(unsigned char)255:(unsigned char)192), alpha);
		int cx = Width/2-ButtonImage->dx/2;
		int cy = Height/2-ButtonImage->dy/2;
		ButtonImage->saypx(x+cx+off, y+cy+off, 0, 0, -1, -1, Multiply8(Alpha, alpha), Rotation, Color);
	}

	return 0;
}

///*********/
///* Label */
///*********/

Label::Label()
{
  mLabel = 0;
  mFont = 0;

  TextColor = 0xffffffff;
  TextAlignment = Align::Left;

  mWordWrap = false;
  mLines.reserve(32);

  UseUserData = 0;
}

void Label::SetWordWrap(bool wrap, int maxWidth)
{
  if (wrap && !mWordWrap)
  {
    if (maxWidth != 0)
      Width = maxWidth;
    RecalculateWordWrap();
  }
  mWordWrap = wrap;
}

void Label::RecalculateWordWrap()
{
  if (!mLabel || !mFont) return;

  /// grrr, stupid visual studio...
  mLines.erase(mLines.begin(), mLines.end());
  mLines.reserve(32);

  Line currentLine;
  currentLine.Start = mLabel;
  int cur = 0;
  int lastSpace = 0;
  int startLine = 0;
  int allLines = 0;

  while (mLabel[cur] > 5)
  {
    if (currentLine.PixelLength + mFont->len(mLabel[cur]) > Width ||
        mLabel[cur] == 10
      )
    {
      allLines++;

      currentLine.PixelLength = 0;
      currentLine.CharLength = 0;

      if (mLabel[cur] == 10)
      {
        lastSpace = cur;
        currentLine.IsWrapped = false;
      } else currentLine.IsWrapped = true;
      if (startLine == lastSpace)
        lastSpace = cur;

      for (int i=startLine; i<lastSpace; i++)
      {
        currentLine.PixelLength += mFont->len(mLabel[i]);
        currentLine.CharLength++;
      }

      currentLine.Start = mLabel+startLine;
      mLines.push_back(currentLine);

      startLine = lastSpace+1;
      if (currentLine.PixelLength + mFont->len(mLabel[cur]) > Width)
        startLine = lastSpace;
      currentLine.PixelLength = 0;

      cur = lastSpace+1;

      continue;
    } else
    {
      currentLine.PixelLength += mFont->len(mLabel[cur]);
    }

    if (mLabel[cur] == L' ')
    {
      lastSpace = cur;
    }

    cur++;
  }

  currentLine.PixelLength = 0;
  currentLine.CharLength = 0;
  currentLine.Start = mLabel+startLine;
  for (int i=startLine; mLabel[i]>5; i++)
  {
    currentLine.PixelLength += mFont->len(mLabel[i]);
    currentLine.CharLength++;
  }
  mLines.push_back(currentLine);
  if (currentLine.PixelLength) allLines++;

  Height = allLines * mFont->get_font_height();
}

void Label::SetLabel( wchar_t* text )
{
  mLabel = text;
  if (mFont && mLabel)
  {
    if (mWordWrap)
    {
      RecalculateWordWrap();
    } else
    {
      Width = mFont->len(mLabel);
      Height = mFont->get_font_height();
    }
  }
}

void Label::SetFont(Font* font)
{
  mFont = font;
  if (mFont && mLabel)
  {
    if (mWordWrap)
    {
      RecalculateWordWrap();
    } else
    {
      Width = mFont->len(mLabel);
      Height = mFont->get_font_height();
    }
  }
}


Label& Label::Set( int x, int y, Font& font, const wchar_t* label, bool wordWrap /*= false*/ )
{
	X = x;
	Y = y;

  mFont = &font;
  mLabel = (wchar_t*)label;
  mWordWrap = wordWrap;

  if (mFont && mLabel)
  {
    if (mWordWrap)
    {
      RecalculateWordWrap();
    } else
    {
      Width = mFont->len(mLabel);
      Height = mFont->get_font_height();
    }
  }

  return *this;
}

Label::~Label()
{
}

uint Label::DrawAt(int x, int y, uint8 alpha)
{
  if (!mLabel || !mFont) return 1;

  if (mWordWrap)
  {
    for (uint i=0; i<mLines.size(); i++)
    {
      int startX = x;
      if (TextAlignment == Align::Center)
        startX = (x+Width/2)-mLines[i].PixelLength/2;
      else if (TextAlignment == Align::Right)
        startX = (x+Width)-mLines[i].PixelLength;

      int lineWidth = 0;
      for (uint c=0; c<mLines[i].CharLength; c++)
      {
        uint16 curChar = *(mLines[i].Start+c);
        mFont->textcpx(curChar, startX+lineWidth, y+i*mFont->get_font_height(), Multiply8(Alpha, alpha), TextColor);
        lineWidth += mFont->len(curChar);
      }
    }
  } else
  {
    if (UseUserData)
    {
      wchar_t* theLabel;
      wchar_t temp[16];
      if (UseUserData == UserDataUsage::Number)
      {
        itow(temp, Userdata[0]);
        theLabel = temp;
      } else if (UseUserData == UserDataUsage::String)
      {
        theLabel = (wchar_t*)Userdata[0];
      }

      int len = mFont->len(theLabel);

      int startX = x;
      if (TextAlignment == Align::Center)
        startX = x-len/2;
      else if (TextAlignment == Align::Right)
        startX = x-len;

      mFont->textpx(theLabel, startX, y, 0, 0, Multiply8(Alpha,alpha), TextColor);
    } else
    {
      /// Works! STFU!
      int startX = x;
      if (TextAlignment == Align::Center)
        startX = x-Width/2;
      else if (TextAlignment == Align::Right)
        startX = x-Width;

      mFont->textpx(mLabel, startX, y, 0, 0, Multiply8(Alpha,alpha), TextColor);
    }
  }

  return 0;
}

///**********/
///* Popup  */
///**********/

Image* Popup::BackgroundGFX;
Image* Popup::ButtonGFX;
Font* Popup::PopupFont;

Popup::Popup() : GUIControl()
{
  /// No initial text in popup, because it forces a word-wrap recalculation
  //mText = txt(AreYouSure);
  mText = 0;
  mButton1Text = txt(OK);
  mButton2Text = txt(Cancel);
  mTwoButtons = false;

  Event1 = 'pop1';
  Event2 = 'pop2';
  TextColor = 0xffffffff;

  Width = Constants::ScreenWidth();
  Height = Constants::ScreenHeight();

  Alpha = 0;
  Visible = false;
  Enabled = false;

  AddChild(&mBackground);
  mBackground.AddChild(&mLabel);
  mBackground.AddChild(&mButton1);
  mBackground.AddChild(&mButton2);

  mBackground.TheImage = Popup::BackgroundGFX;
	mBackground.SetDimsFromImage();
  mBackground.AlignToParent(Align::Center, Align::Center);

	mLabel.Set(20,20,*Popup::PopupFont,L"", false);
  mLabel.SetWordWrap(true, mBackground.TheImage->dx-40);
  mLabel.TextAlignment = Align::Center;
  //mLabel.AlignToParent(Align::Center)
	
	int w = ((Popup::ButtonGFX->dx*2)+20)/2;
	int h = Popup::ButtonGFX->dy/4+20;
  
  mButton1.Set(*Popup::PopupFont, *Popup::ButtonGFX, mButton1Text, mBackground.Width/2-w, mBackground.Height-h);
  mButton2.Set(*Popup::PopupFont, *Popup::ButtonGFX, mButton2Text, (mBackground.Width/2-w)+Popup::ButtonGFX->dx+20, mBackground.Height-h);

  this->SetEventBuffer(&mOwnEventBuffer);
}

Popup& Popup::Set( wchar_t* text, uint ev1, uint ev2, wchar_t* but1, wchar_t* but2 /*= 0*/ )
{
  mText = text;
  mLabel.SetLabel(mText);
  mLabel.AlignToParent(Align::Center);

  Event1 = ev1;
  Event2 = ev2;

  mButton1Text = but1;
  mButton1.SetLabel(but1);
  mButton2Text = but2;
  if (but2)
  {
    mButton2.SetLabel(but2);
    mTwoButtons = true;
  } else
	{
		mButton2.Hide();
		mButton1.AlignToParent(Align::Center);
	}

  return *this;
}

void Popup::UpdateAt(int dt)
{
	bool added = false;
  for (uint i=0; i<mEvents->Count; i++)
  {
		Event* event = &mEvents->Events[i];
    if (event->EventID == GUIEvent::Click)
    {
      if (event->Caller == &mButton1)
      {
        mParent->AddEvent(Event1, &mButton1);
        Hide();
				added = true;
      }
      if (event->Caller == &mButton2)
      {
        mParent->AddEvent(Event2, &mButton2);
        Hide();
				added = true;
      }
	  }

		if (!added)	
			mParent->AddEvent(event->EventID, (DisplayObject*)event->Caller);
  }
  mEvents->ClearEvents();
}

Popup::~Popup()
{
}

void Popup::Show()
{
  Animate.FadeIn(30);
  Enabled = true;
}

void Popup::Hide()
{
  Animate.FadeOut(30);
  Enabled = false;
}

void Popup::SetLabel( wchar_t* text )
{
	mText = text;
	mLabel.SetLabel(mText);
	mLabel.AlignToParent(Align::Center);
}

///************/
///* Selecter */
///************/

struct selPar { DisplayObject* parent; Selecter* self; };

lambda(DeselectSelecters, selPar, DisplayObject*)
if (child->Is('sele') && child != mParent->self)
{
  Selecter* s = (Selecter*)child;
  s->Deselect();
}
end_lambda;

void Selecter::Select()
{
  mSelected = true;
  //Effects.Glow.Enabled = true;
	Animate.Pulse(90, 0.08f, 0.08f);
	//Animate.Bounce(50, 10);

  selPar s = { mParent, this };
  DeselectSelecters ds(&s);
  mParent->ForEachChild<DeselectSelecters>(ds);
}

void Selecter::Deselect()
{
  mSelected = false;
  //Effects.Glow.Enabled = false;

	Animate.Pulse(0);
	//Animate.Bounce(0);
}

Selecter::Selecter() : SelectGroup(0), mSelected(false)
{
}

Selecter::~Selecter()
{

}

Selecter& Selecter::Set( uint group, Image& image, int x, int y )
{
  SelectGroup = group;
  TheImage = &image;
  X = x;
  Y = y;
  SetDimsFromImage();

  return *this;
}

bool Selecter::OnClick()
{
  AddEvent(GUIEvent::Click);
  Select();
  return true;
}

///************************************************************************/
///* Checker                                                              */
///************************************************************************/

void Checker::Check()
{
	mChecked = true;
	Effects.Glow.Enabled = true;
}

void Checker::Uncheck()
{
	mChecked = false;
	Effects.Glow.Enabled = false;
}

Checker::Checker() : mChecked(false)
{
}

Checker::~Checker()
{

}

Checker& Checker::Set( Image& image, int x, int y )
{
	TheImage = &image;
	X = x;
	Y = y;
	SetDimsFromImage();

	return *this;
}

bool Checker::OnClick()
{
	AddEvent(GUIEvent::Click);
	if (mChecked)
		Uncheck();
	else
		Check();
	return true;
}

///************************************************************************/
///* TextBox                                                              */
///************************************************************************/

void TextBox::SetFont( Font* font )
{
  mFont = font;
}

TextBox::TextBox() : GUIControl(), TextColor(0xffffffff), Text(), MarginLeft(0), MarginTop(0), BoxWidth(Width),
  mFont(0), mCursorPos(0), mDrawOffset(0), mCursorInside(false)
{
}

TextBox::~TextBox()
{

}

uint TextBox::DrawAt( int x, int y, uint8 alpha )
{
  if (TheImage)
    TheImage->saypx(x, y, 0, 0, Width, Height, Multiply8(Alpha, alpha), Rotation, Color);
  if (mFont)
  {
    mFont->textpx(Text.c_str(), (x+MarginLeft)-mDrawOffset, y+MarginTop, 0, 0, Multiply8(Alpha, alpha), Color, 0, BM_ALPHA, x+MarginLeft, y+MarginTop, BoxWidth, Height-MarginTop);
  }
  return 0;
}

TextBox& TextBox::Set( Image& image, int x, int y, Font& font, int marginLeft /*= 0*/, int marginRight /*= 0*/, int boxWidth /*= -1*/ )
{
  X = x;
  Y = y;
  mFont = &font;
  TheImage = &image;
  SetDimsFromImage();
  MarginLeft = marginLeft;
  MarginTop = marginRight;
  if (boxWidth >= 0)
    BoxWidth = boxWidth;
  else
    BoxWidth = Width;

  return *this;
}

bool TextBox::OnMouseUp( int x, int y )
{
  if (mPressed && IsPointInside(x, y, true))
  {
    AddEvent(GUIEvent::MouseUp);
    mCursorInside = true;
    mPressed = false;
    return OnClick();
  }

  if (!IsPointInside(x, y, true))
  {
    mCursorInside = false;
  }

  mPressed = false;
  return false;
}

void TextBox::UpdateAt( int dt )
{
  if (KeyPressed(8) && !KeyPressed_Old(8))
  {
    if (!Text.empty()) Text.resize(Text.size()-1);
  }
  for (wchar_t i=32; i<128; i++)
  {
    if (KeyPressed(i) && !KeyPressed_Old(i))
      Text += (wchar_t)i  ;
  }
}